package com.evil.application.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.account.api.RemoteUserEnterpriseService;
import com.evil.account.api.RemoteUserService;
import com.evil.application.api.RemoteApplicationReportingService;
import com.evil.application.enums.reporting.*;
import com.evil.application.mapper.ApplicationInfoMapper;
import com.evil.application.mapper.ApplicationReportingMapper;
import com.evil.application.pojo.dto.form.control.code.config.FilterConfigDTO;
import com.evil.application.pojo.dto.permission.control.ModifyControlPermissionReqDTO;
import com.evil.application.pojo.dto.reporting.*;
import com.evil.application.pojo.entity.ApplicationForm;
import com.evil.application.pojo.entity.ApplicationInfo;
import com.evil.application.pojo.entity.ApplicationReporting;
import com.evil.application.service.*;
import com.evil.application.util.ApplicationReportingUtil;
import com.evil.application.util.ApplicationUtil;
import com.evil.common.account.dto.user.UserBaseRespDTO;
import com.evil.common.account.util.LoginUtil;
import com.evil.common.application.dto.form.control.ControlBaseDTO;
import com.evil.common.application.dto.judge.JudgesDTO;
import com.evil.common.application.dto.permission.app.AppPermissionSourceDTO;
import com.evil.common.application.dto.permission.app.AppPermissionsListDTO;
import com.evil.common.application.dto.permission.app.CopyAppPermissionReqDTO;
import com.evil.common.application.dto.permission.app.ModifyAppPermissionReqDTO;
import com.evil.common.application.dto.permission.control.ControlPermissionSourceDTO;
import com.evil.common.application.enums.*;
import com.evil.common.application.enums.form.ControlPermissionSourceTypeEnum;
import com.evil.common.application.enums.judge.JudgeSourceTypeEnum;
import com.evil.common.application.enums.permission.PermissionStatusEnum;
import com.evil.common.core.dto.FindByIdsReqDTO;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.enums.SpecialFieldEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.StreamUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 应用报表表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class ApplicationReportingServiceImpl extends ServiceImpl<ApplicationReportingMapper, ApplicationReporting>
        implements ApplicationReportingService, RemoteApplicationReportingService {

    private final LoginUtil loginUtil;

    private final ApplicationReportingUtil applicationReportingUtil;

    private final ApplicationInfoMapper applicationInfoMapper;

    private final ApplicationFormService applicationFormService;

    private final ApplicationFormControlService applicationFormControlService;

    private final ApplicationPermissionService applicationPermissionService;

    private final ApplicationJudgeService applicationJudgeService;

    private final ControlPermissionService controlPermissionService;

    private final RemoteUserService remoteUserService;

    private final RemoteUserEnterpriseService remoteUserEnterpriseService;

    private final ApplicationLogServiceImpl logService;

    /**
     * 报表枚举
     *
     * @param applicationKey applicationKey
     * @return Map
     */
    @Override
    public Map<String, List<?>> types(String applicationKey) {
        ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(applicationKey);
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        ApplicationForm form = applicationFormService.releaseForm(applicationInfo);
        List<ControlBaseDTO> controls = applicationFormControlService.findBaseByForm(form, true);

        Map<String, List<?>> typesMap = new HashMap<>();
        // 报表指标类型
        typesMap.put("types", ReportingTypeEnum.types());
        // 报表指标类型
        typesMap.put("targetTypes", ReportingTargetTypeEnum.types(controls));
        // 报表时间单位
        typesMap.put("dateUnitTypes", ReportingDateUnitEnum.types());
        // 报表排名类型
        typesMap.put("rankingTypes", ReportingRankingTypeEnum.types());
        // 报表样式类型
        typesMap.put("styleTypes", ReportingStyleTypeEnum.types());
        // 显示格式
        typesMap.put("formatTypes", FormatTypeEnum.types());
        // 排序方式
        typesMap.put("sortTypes", SortTypeEnum.types());
        // 汇总方式
        typesMap.put("summaryTypes", SummaryTypeEnum.types());
        // 货币单位
        typesMap.put("currentCodesMap", CurrencyCodeEnum.codesMap());

        return typesMap;
    }

    /**
     * 应用报表基础信息列表
     *
     * @param appReportingBasesReqDTO appReportingBasesReqDTO
     * @return List
     */
    @Override
    public List<ApplicationReportingBaseDTO> appReportingBases(AppReportingBasesReqDTO appReportingBasesReqDTO) {
        ApplicationInfo app = applicationInfoMapper.findByKey(appReportingBasesReqDTO.getApplicationKey());
        // 检查企业
        loginUtil.checkEnterpriseId(app.getEnterpriseId());

        List<ApplicationReporting> reportingList = this.baseMapper.findByApplicationId(app.getApplicationId());
        // 过滤无权查看
        reportingList = StreamUtil.filter(reportingList, e -> applicationPermissionService.loginUserHasPermission(
                app.getEnterpriseId(),
                e.getPermissionStatus(),
                new AppPermissionSourceDTO(ApplicationSourceTypeEnum.REPORTING, e.getReportingId()),
                false));
        // 获取创建人信息
        Set<Long> creatorIds = StreamUtil.transSetT(reportingList, ApplicationReporting::getCreatorId);
        Map<Long, UserBaseRespDTO> userBaseMap = remoteUserService.findBaseMapByIds_Default(FindByIdsReqDTO.unFilter(creatorIds));
        // 转换返回实体
        return StreamUtil.transListT(reportingList, e -> {
            ApplicationReportingBaseDTO reportingBaseDTO = new ApplicationReportingBaseDTO(e);

            reportingBaseDTO.setApplicationKey(app.getApplicationKey());
            // 创建人
            reportingBaseDTO.setCreator(userBaseMap.get(e.getCreatorId()));
            return reportingBaseDTO;
        });
    }

    /**
     * 应用报表基础信息
     *
     * @param reportingKey reportingKey
     * @return ApplicationReportingRespDTO
     */
    @Override
    public ApplicationReportingBaseDTO appReportingBaseInfo(String reportingKey) {
        // 获取报表详情
        return applicationReportingUtil.appReportingInfo(reportingKey);
    }

    /**
     * 应用报表信息
     *
     * @param reportingKey reportingKey
     * @return ApplicationReportingRespDTO
     */
    @Override
    public ApplicationReportingDTO appReportingInfo(String reportingKey) {
        // 获取报表详情
        return applicationReportingUtil.appReportingInfo(reportingKey);
    }

    /**
     * 新增应用报表
     *
     * @param addReportingReqDTO addReportingReqDTO
     * @return AddReportingRespDTO
     */
    @Override
    public AddReportingRespDTO addReporting(AddReportingReqDTO addReportingReqDTO) {
        String reportingKey = addReportingReqDTO.getReportingKey();
        if (StringUtils.isNotBlank(reportingKey)) {
            reportingKey = addReportingReqDTO.getReportingKey().trim();
            if (reportingKey.length() < 8 || reportingKey.length() > 32) {
                throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "报表key错误");
            }
        }

        ApplicationInfo app = applicationInfoMapper.findByKey(addReportingReqDTO.getApplicationKey());
        // 检查企业
        loginUtil.checkEnterpriseId(app.getEnterpriseId());
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        ApplicationReportingDTO applicationReportingRespDTO = new ApplicationReportingDTO();
        applicationReportingRespDTO.setApplicationId(app.getApplicationId());
        applicationReportingRespDTO.setReportingName(addReportingReqDTO.getReportingName());
        // 生成key
        if (StringUtils.isBlank(reportingKey)) {
            reportingKey = ApplicationUtil.generateKey(key -> applicationInfoMapper.isExistByKey(key).isPresent());
        }
        applicationReportingRespDTO.setReportingKey(reportingKey);
        applicationReportingRespDTO.setPermissionStatus(addReportingReqDTO.getPermissionStatus());

        // 创建实体
        ApplicationReporting reporting = applicationReportingRespDTO.createApplicationReporting();
        reporting.setEnterpriseId(app.getEnterpriseId());
        logService.saveAndCheck(this.baseMapper, ApplicationReporting::getReportingId, reporting);

        // 保存报表权限
        this.saveApplicationPermission(reporting, addReportingReqDTO);

        AddReportingRespDTO addReportingRespDTO = new AddReportingRespDTO();
        addReportingRespDTO.setReportingKey(reporting.getReportingKey());
        addReportingRespDTO.setReportingName(reporting.getReportingName());
        return addReportingRespDTO;
    }

    /**
     * 复制应用报表
     *
     * @param reportingKey reportingKey
     * @return AddReportingRespDTO
     */
    @Override
    public AddReportingRespDTO copyReporting(String reportingKey) {
        ApplicationReporting reporting = this.baseMapper.findByKey(reportingKey);

        ApplicationInfo app = applicationInfoMapper.findById(reporting.getApplicationId());
        // 检查企业
        loginUtil.checkEnterpriseId(app.getEnterpriseId());
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        // 复制实体
        ApplicationReporting copyReporting = BeanUtil.copyProperties(reporting, ApplicationReporting.class, SpecialFieldEnum.getSpecialFields());
        copyReporting.setReportingId(null);
        copyReporting.setReportingName(String.format("副本-%s", copyReporting.getReportingName()));
        // 生成key
        String copyKey = ApplicationUtil.generateKey(key -> applicationInfoMapper.isExistByKey(key).isPresent());
        copyReporting.setReportingKey(copyKey);
        logService.saveAndCheck(this.baseMapper, ApplicationReporting::getReportingId, copyReporting);

        // 复制权限
        Long sourceId = reporting.getReportingId();
        Long targetId = copyReporting.getReportingId();
        Long enterpriseId = reporting.getEnterpriseId();
        applicationPermissionService.copyPermission(new CopyAppPermissionReqDTO(ApplicationSourceTypeEnum.REPORTING, enterpriseId, sourceId, targetId));

        AddReportingRespDTO addReportingRespDTO = new AddReportingRespDTO();
        addReportingRespDTO.setReportingKey(copyReporting.getReportingKey());
        addReportingRespDTO.setReportingName(copyReporting.getReportingName());
        return addReportingRespDTO;
    }

    /**
     * 删除应用报表
     *
     * @param reportingKey reportingKey
     */
    @Override
    public void deleteReporting(String reportingKey) {
        ApplicationReporting reporting = this.baseMapper.findByKey(reportingKey);

        ApplicationInfo app = applicationInfoMapper.findById(reporting.getApplicationId());
        // 检查企业
        loginUtil.checkEnterpriseId(app.getEnterpriseId());
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        reporting.setIsDeleted(SwitchEnum.YES.getId());
        logService.saveAndCheck(this.baseMapper, ApplicationReporting::getReportingId, reporting);
    }

    /**
     * 编辑应用报表权限
     *
     * @param modifyReportingPermissionReqDTO modifyReportingPermissionReqDTO
     */
    @Override
    public void modifyReportingPermission(ModifyReportingPermissionReqDTO modifyReportingPermissionReqDTO) {
        String reportingKey = modifyReportingPermissionReqDTO.getReportingKey();
        ApplicationReporting reporting = this.baseMapper.findByKey(reportingKey);

        ApplicationInfo app = applicationInfoMapper.findById(reporting.getApplicationId());
        // 检查企业
        loginUtil.checkEnterpriseId(app.getEnterpriseId());
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        reporting.setPermissionStatus(modifyReportingPermissionReqDTO.getPermissionStatus());
        logService.saveAndCheck(this.baseMapper, ApplicationReporting::getReportingId, reporting);

        // 保存报表权限
        this.saveApplicationPermission(reporting, modifyReportingPermissionReqDTO);
    }

    /**
     * 编辑应用报表
     *
     * @param applicationReportingDTO applicationReportingDTO
     */
    @Override
    public ApplicationReportingBaseDTO modifyReporting(ApplicationReportingDTO applicationReportingDTO) {
        String reportingKey = applicationReportingDTO.getReportingKey();
        ApplicationReporting reporting = this.baseMapper.findByKey(reportingKey);

        ApplicationInfo app = applicationInfoMapper.findById(reporting.getApplicationId());
        // 检查企业
        loginUtil.checkEnterpriseId(app.getEnterpriseId());
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        // 应用表单
        Map<Long, String> controlCodeMap = applicationFormControlService.controlCodeMap(app.getApplicationId(),
                ApplicationVersionEnum.RELEASE_VERSION, true);

        // 检查属性
        this.checkReportingProps(controlCodeMap, applicationReportingDTO);

        // 保存报表权限
        this.saveApplicationPermission(reporting, applicationReportingDTO);

        // 保存筛选条件
        if (SwitchEnum.isOpen(applicationReportingDTO.getFilterStatus())) {
            FilterConfigDTO filterConfig = applicationReportingDTO.getFilterConfig();
            applicationJudgeService.saveJudges(new JudgesDTO(JudgeSourceTypeEnum.REPORTING_FILTER_CONDITION,
                    reporting.getEnterpriseId(), reporting.getReportingId(), filterConfig.getJudgeOrs()));
        }

        // 保存控件权限
        if (SwitchEnum.isOpen(applicationReportingDTO.getControlPermissionStatus())) {
            ModifyControlPermissionReqDTO modifyNodeControlSettingDTO = new ModifyControlPermissionReqDTO();
            modifyNodeControlSettingDTO.setControlPermissionSourceDTO(new ControlPermissionSourceDTO(
                    ControlPermissionSourceTypeEnum.REPORTING, reporting.getReportingId()));
            modifyNodeControlSettingDTO.setControlPermissions(applicationReportingDTO.getControlPermissions());
            modifyNodeControlSettingDTO.setControlIds(new ArrayList<>(controlCodeMap.keySet()));
            controlPermissionService.saveControlPermissions(modifyNodeControlSettingDTO);
        }

        // 复制属性
        applicationReportingDTO.copyProps(reporting);
        logService.saveAndCheck(this.baseMapper, ApplicationReporting::getReportingId, reporting);

        return this.appReportingBaseInfo(reportingKey);
    }

    /**
     * 删除应用所有报表
     *
     * @param applicationInfo applicationInfo
     */
    @Override
    public void deleteByApp(ApplicationInfo applicationInfo) {
        List<ApplicationReporting> applicationReportingList = this.baseMapper.findByApplicationId(applicationInfo.getApplicationId()).stream()
                .peek(e -> e.setIsDeleted(SwitchEnum.YES.getId()))
                .collect(Collectors.toList());
        logService.saveAndCheck(this.baseMapper, ApplicationReporting::getReportingId, applicationReportingList);
    }

    /**
     * 保存报表权限
     *
     * @param applicationReporting  applicationReporting
     * @param appPermissionsListDTO appPermissionsListDTO
     */
    private void saveApplicationPermission(ApplicationReporting applicationReporting, AppPermissionsListDTO appPermissionsListDTO) {
        if (PermissionStatusEnum.ASSIGN.getId() == appPermissionsListDTO.getPermissionStatus()) {
            ModifyAppPermissionReqDTO modifyAppPermissionReqDTO = new ModifyAppPermissionReqDTO();
            modifyAppPermissionReqDTO.setSourceType(ApplicationSourceTypeEnum.REPORTING.getId());
            modifyAppPermissionReqDTO.setSourceId(applicationReporting.getReportingId());
            modifyAppPermissionReqDTO.setPermissions(appPermissionsListDTO.getPermissions());
            applicationPermissionService.modifyPermission(modifyAppPermissionReqDTO);
        }
    }

    /**
     * 检查报表配置
     *
     * @param controlCodeMap          controlCodeMap
     * @param applicationReportingDTO applicationReportingDTO
     */
    private void checkReportingProps(Map<Long, String> controlCodeMap, ApplicationReportingDTO applicationReportingDTO) {
        // 报表维度（行、表头）
        if (CollectionUtil.isNotEmpty(applicationReportingDTO.getReportingHeaders())) {
            Set<ReportingHeaderDTO> headers = new HashSet<>();
            applicationReportingDTO.getReportingHeaders().forEach(e -> {
                if (!headers.add(e)) {
                    throw new BusinessException(RCodeEnum.APPLICATION_REPORTING_HEADERS_REPEAT);
                }
                if (!controlCodeMap.containsKey(e.getControlId())) {
                    throw new BusinessException(RCodeEnum.APPLICATION_REPORTING_HEADERS_ERROR);
                }
            });
        }
        // 报表维度（列）
        if (CollectionUtil.isNotEmpty(applicationReportingDTO.getReportingColumnHeaders())) {
            Set<ReportingHeaderDTO> headers = new HashSet<>();
            applicationReportingDTO.getReportingColumnHeaders().forEach(e -> {
                if (!headers.add(e)) {
                    throw new BusinessException(RCodeEnum.APPLICATION_REPORTING_COLUMN_HEADERS_REPEAT);
                }
                if (!controlCodeMap.containsKey(e.getControlId())) {
                    throw new BusinessException(RCodeEnum.APPLICATION_REPORTING_COLUMN_HEADERS_ERROR);
                }
            });
        }
        // 指标
        if (CollectionUtil.isNotEmpty(applicationReportingDTO.getReportingTargets())) {
            Set<ReportingTargetDTO> targets = new HashSet<>();
            applicationReportingDTO.getReportingTargets().forEach(e -> {
                if (!targets.add(e)) {
                    throw new BusinessException(RCodeEnum.APPLICATION_REPORTING_TARGET_REPEAT);
                }
                if (!e.checkControl()) {
                    if (!controlCodeMap.containsKey(e.getControlId())) {
                        throw new BusinessException(RCodeEnum.APPLICATION_REPORTING_TARGET_ERROR);
                    }
                }
                // 检查指标属性
                e.checkProps();
            });
        }
        // 排序配置.
        if (CollectionUtil.isNotEmpty(applicationReportingDTO.getSorts())) {
            if (applicationReportingDTO.getSorts().stream().anyMatch(e -> !controlCodeMap.containsKey(e.getControlId()))) {
                throw new BusinessException(RCodeEnum.APPLICATION_REPORTING_SORT_ERROR);
            }
        }
        // 排名
        if (SwitchEnum.isOpen(applicationReportingDTO.getRankingStatus())) {
            ReportingRankingConfigDTO rankingConfig = applicationReportingDTO.getRankingConfig();
            if (Objects.isNull(rankingConfig.getRankingType())) {
                throw new BusinessException(RCodeEnum.APPLICATION_REPORTING_RANKING_ERROR);
            }
            if (Objects.isNull(rankingConfig.getRanking())) {
                throw new BusinessException(RCodeEnum.APPLICATION_REPORTING_RANKING_ERROR);
            }
        }
        // 样式类型
        if (Objects.nonNull(applicationReportingDTO.getStyleType())) {
            ReportingStyleTypeEnum.isExistById(applicationReportingDTO.getStyleType());
        }
        // 筛选条件
        FilterConfigDTO filterConfigDTO = applicationReportingDTO.getFilterConfig();
        if (CollectionUtil.isNotEmpty(filterConfigDTO.getJudgeOrs())) {
            applicationJudgeService.checkJudge(filterConfigDTO.getJudgeOrs(), controlCodeMap, null);
        }
    }
}
